package com.wgworkshop.pems.common;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.data.repository.CrudRepository;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 通用工具类。
 * Created by wlc on 2017/7/3.
 */
public class CommonUtils {
    public static String[] getNullPropertyNames(Object bean) {
        BeanWrapper wrapper = new BeanWrapperImpl(bean);
        PropertyDescriptor[] descriptors = wrapper.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor descriptor :
                descriptors) {
            String name = descriptor.getName();
            Object value = wrapper.getPropertyValue(name);
            if (value == null) {
                emptyNames.add(name);
            }
        }

        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    public static <T, S> List<T> convertIterableToList(Iterable<S> sources, Class<T> targetClass) {
        List<T> targets = new ArrayList<>();
        try {
            for (S source : sources) {
                T target = targetClass.newInstance();
                BeanUtils.copyProperties(source, target);
                targets.add(target);
            }
        } catch (IllegalAccessException | InstantiationException ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }

        return targets;
    }

    public static <M, E, ID extends Serializable, R extends CrudRepository<E, ID>>
    ResponseData<M> modifyFieldsFromEntity(R repo, Class<M> modelClass, M model, ID id) {
        return modifyFieldsFromEntity(repo, modelClass, model, id, true);
    }

    public static <M, E, ID extends Serializable, R extends CrudRepository<E, ID>>
    ResponseData<M> modifyFieldsFromEntity(R repo, Class<M> modelClass, M model, ID id, boolean withNull) {
        M resultModel;
        try {
            E entity = repo.findOne(id);
            if (withNull) {
                BeanUtils.copyProperties(model, entity);
            } else {
                BeanUtils.copyProperties(model, entity, getNullPropertyNames(model));
            }
            E resultEntity = repo.save(entity);
            resultModel = modelClass.newInstance();
            BeanUtils.copyProperties(resultEntity, resultModel);
        } catch (IllegalAccessException | InstantiationException ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }

        return new ResponseData<M>().success(resultModel);
    }

    public static Date toDate(String str) {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        Date result = null;
        try {
            result = format.parse(str);
        } catch (ParseException e) {
            // do noting
        }
        return result;
    }

    /**
     * 计算离当前日期最近的指定周<code>week</code>，并调整<code>shift</code>周后为基准的范围边界日期。
     * 范围定义为以基准日期开始向过去推进<code>before</code>周，向未来推进<code>after</code>周。
     *
     * @param week   指定周（0 BASED，周日=0，周六=6）
     * @param shift  调整周
     * @param before 向过去推进周数
     * @param after  向未来推进周数
     * @return 边界日期组合
     */
    public static DateBoundary calculateBoundaryDate(int week, int shift, int before, int after) {
        if (week < 0 || week > 6) {
            throw new IllegalArgumentException(String.format("不正确的指定周:week=%s", week));
        }
        if (before < 0 || after < 0) {
            throw new IllegalArgumentException(String.format("不正确的推进范围:before=%s,after=%s", before, after));
        }

        Calendar today = Calendar.getInstance();

        // 移动当前日期到未来最近的一个和指定周匹配的日期
        int currentWeek = today.get(Calendar.DAY_OF_WEEK) - 1;
        if (week >= currentWeek) {
            today.add(Calendar.DATE, week - currentWeek);
        } else {
            today.add(Calendar.DATE, 7 + week - currentWeek);
        }

        today.add(Calendar.DATE, shift * 7);

        // 移动当前日期到过去的before周~未来after周
        today.add(Calendar.DATE, -before * 7);
        Date start = today.getTime();
        today.add(Calendar.DATE, (before + after) * 7);
        Date end = today.getTime();

        DateBoundary boundary = new DateBoundary();
        boundary.start = start;
        boundary.end = end;

        return boundary;
    }

    public static class DateBoundary {
        public Date start;
        public Date end;
    }
}
